package org.zachary.agent.domain.aggregate_model.token;

import lombok.Getter;
import org.springframework.data.domain.AbstractAggregateRoot;
import org.zachary.agent.domain.aggregate_model.token.event.TokenGeneratedEvent;
import org.zachary.agent.domain.aggregate_model.token.event.TokenInvalidatedEvent;
import org.zachary.agent.domain.aggregate_model.token.event.TokenRefreshedEvent;
import org.zachary.agent.domain.exception.TokenException;
import org.zachary.agent.domain.service.TokenService;

import java.io.Serial;
import java.io.Serializable;
import java.util.Map;

public class Token extends AbstractAggregateRoot<Token> implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;

    @Getter
    private String id;
    @Getter
    private String userId;
    private DeviceId deviceId;
    private AccessToken accessToken;
    private RefreshToken refreshToken;
    @Getter
    private TokenStatus status;
    public Token(String id,String userId,DeviceId deviceId,AccessToken accessToken, RefreshToken refreshToken) {
        this.id = id;
        this.userId = userId;
        this.deviceId = deviceId;
        this.accessToken = accessToken;
        this.refreshToken = refreshToken;
        this.status = TokenStatus.ACTIVE;
    }
    private Token(String id,String userId, DeviceId deviceId, AccessToken accessToken, RefreshToken refreshToken,TokenStatus status){
        this.id = id;
        this.userId = userId;
        this.deviceId = deviceId;
        this.accessToken = accessToken;
        this.refreshToken = refreshToken;
        this.status = status;
    }
    protected Token() {}
    public Token generate(String id,String userId, DeviceId deviceId, TokenService tokenService) {
        AccessToken accessToken = tokenService.generateAccessToken(userId);
        RefreshToken refreshToken = tokenService.generateRefreshToken(userId, deviceId);
        Token token = new Token(id,userId, deviceId, accessToken, refreshToken);
        registerEvent(new TokenGeneratedEvent(this,userId, deviceId));
        return token;
    }
    public void refresh(TokenService tokenService) throws Exception {
        if (this.status != TokenStatus.ACTIVE) {
            throw new TokenException("Token已失效");
        }
        Map<String,Object> map= tokenService.rotateRefreshToken(this.refreshToken.getRefreshToken(),this.userId, deviceId);
        AccessToken access= (AccessToken) map.get("access");
        RefreshToken refresh= (RefreshToken) map.get("refresh");
        this.accessToken = new AccessToken(access.getAccessToken(),access.getAccessExpires());
        this.refreshToken = new RefreshToken(refresh.getRefreshToken(),refresh.getRefreshExpires());
        registerEvent(new TokenRefreshedEvent(this,userId));
        invalidate();
    }
    public void invalidate() {
        this.status = TokenStatus.INACTIVE;
        registerEvent(new TokenInvalidatedEvent(this, "MANUAL_REVOCATION"));
    }
    public static Token rebuild(String id,String userId, DeviceId deviceId, AccessToken accessToken, RefreshToken refreshToken,TokenStatus status) {
        return new Token(id,userId,deviceId,accessToken,refreshToken,status);
    }

    public String getDeviceId() {
        return deviceId.getDeviceId();
    }
    public String getAccessToken() {
        return accessToken.getAccessToken();
    }
    public long getAccessExpires() {
        return accessToken.getAccessExpires();
    }
    public String getRefreshToken() {
        return refreshToken.getRefreshToken();
    }
    public long getRefreshExpires() {
        return refreshToken.getRefreshExpires();
    }
}
